<template>
  <view class="l-echart" :style="customStyle" v-if="canvasId">
    <!-- #ifndef APP-NVUE -->
    <canvas
      class="l-echart__canvas"
      v-if="use2dCanvas"
      type="2d"
      :id="canvasId"
      :style="'width:' + width + 'px;height:' + height + 'px'"
      :disable-scroll="isDisableScroll"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    />
    <canvas
      class="l-echart__canvas"
      v-else
      :width="nodeWidth"
      :height="nodeHeight"
      :style="'width:' + width + 'px;height:' + height + 'px'"
      :canvas-id="canvasId"
      :id="canvasId"
      :disable-scroll="isDisableScroll"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    />
    <!-- #endif -->
    <!-- #ifdef APP-NVUE -->
    <web-view
      class="l-echart__canvas"
      :id="canvasId"
      :webview-styles="webviewStyles"
      ref="webview"
      src="http://liangei.gitee.io/limeui/hybrid/html/lime-ui/lime-echart/index.html?v=0.2.3"
      @pagefinish="finished = true"
      @onPostMessage="onMessage"
    ></web-view>
    <!-- #endif -->
  </view>
</template>
<script>
// #ifndef APP-NVUE
import Canvas from './canvas';
import * as echarts from './echarts';
import { compareVersion, wrapTouch, devicePixelRatio } from './utils';
// #endif
// #ifdef APP-NVUE
import { base64ToPath } from './utils';
// #endif
export default {
  // version: '0.2.6' 
  name: 'l-echart',
  props: {
    // #ifdef MP-WEIXIN || MP-TOUTIAO
    type: {
      type: String,
      default: '2d'
    },
    // #endif
    ec: {
      type: Object,
      default: () => { }
    },
    onInit: Function,
    // #ifdef APP-NVUE
    webviewStyles: Object,
    params: {
      type: Object,
      default: () => { }
    },
    // #endif
    customStyle: String,
    isAutoPlay: Boolean,
    isDisableScroll: Boolean,
    isEnable: Boolean,
    isClickable: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      // #ifdef MP-WEIXIN || MP-TOUTIAO
      use2dCanvas: true,
      // #endif
      // #ifndef MP-WEIXIN || MP-TOUTIAO
      use2dCanvas: false,
      // #endif
      width: null,
      height: null,
      nodeWidth: null,
      nodeHeight: null,
      canvasNode: null,
      config: {},
      inited: false,
      finished: false,
      file: '',
      platform: ''
    };
  },
  computed: {
    canvasId () {
      return `l-echart${this._uid}`
    }
  },
  watch: {
    'ec.option': {
      deep: true,
      handler (val, oldVal) {
        if (this.isAutoPlay) {
          if (this.ec.clear) { this.clear() }
          this.setOption(val);
        }
      }
    }
  },
  beforeDestroy () {
    this.clear()
    this.dispose()
  },
  // #ifndef APP-NVUE
  created () {
    echarts.registerPreprocessor(option => {
      if (option && option.series) {
        if (option.series.length > 0) {
          option.series.forEach(series => {
            series.progressive = 0;
          });
        } else if (typeof option.series === 'object') {
          option.series.progressive = 0;
        }
      }
    });
    // #ifdef MP-WEIXIN || MP-TOUTIAO
    const { SDKVersion, version, platform, environment } = uni.getSystemInfoSync();
    // #endif
    // #ifdef MP-WEIXIN
    this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '2.9.2') >= 0 && !((/ios/i.test(platform) && /7.0.20/.test(version)) || /wxwork/i.test(environment)) && !/windows/i.test(platform);
    this.platform = /windows/i.test(platform) ? platform : ''
    // #endif
    // #ifdef MP-TOUTIAO
    this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '1.78.0') >= 0;
    // #endif
  },
  // #endif
  mounted () {
    if (this.ec && !this.ec?.lazyLoad || this.isEnable) {
      if (!this.onInit) {
        this.init();
        return
      }
    }
    if (this.onInit) {
      this.init(this.onInit)
    }
  },
  methods: {
    // #ifdef APP-NVUE
    getWebview () {
      if (this.finished) {
        return Promise.resolve(this.finished)
      }
      return new Promise(resolve => {
        this.$watch('finished', (val) => {
          if (val) {
            resolve(this.finished)
          }
        })
      })
    },
    onMessage (e) {
      const res = e?.detail?.data[ 0 ] || null;
      if (res?.event) {
        this.$emit(res.event, JSON.parse(res.data));
      } else if (res?.file) {
        this.file = res.data
      } else {
        console.error(res);
      }
    },
    // #endif
    async setChart (callback, params) {
      // #ifndef APP-NVUE
      await this.enabled()
      if (typeof callback === 'function' && this.chart) {
        await callback(this.chart);
      }
      // #endif
      // #ifdef APP-NVUE
      await this.getWebview()
      if (typeof callback === 'function') {
        this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(params || this.params)})`);
      }
      // #endif
    },
    async setOption (val) {
      // #ifndef APP-NVUE
      await this.enabled()
      if (!this.chart || !this.chart.setOption) {
        console.warn(`组件还未初始化`)
      } else {
        this.chart.setOption(val);
      }
      // #endif
      // #ifdef APP-NVUE
      await this.getWebview()
      if (val) {
        this.$refs.webview.evalJs(`setOption(${JSON.stringify(val)}`);
      }
      // #endif
    },
    async showLoading () {
      // #ifndef APP-NVUE
      await this.enabled()
      if (this.chart) {
        this.chart.showLoading()
      }
      // #endif
      // #ifdef APP-NVUE
      this.$refs.webview.evalJs(`showLoading()`);
      // #endif
    },
    hideLoading () {
      // #ifndef APP-NVUE
      if (this.chart) {
        this.chart.hideLoading()
      }
      // #endif
      // #ifdef APP-NVUE
      this.$refs.webview.evalJs(`hideLoading()`);
      // #endif
    },
    clear () {
      // #ifndef APP-NVUE
      if (this.chart) {
        this.chart.clear()
      }
      // #endif
      // #ifdef APP-NVUE
      this.$refs.webview.evalJs(`clear()`);
      // #endif
    },
    dispose () {
      // #ifndef APP-NVUE
      if (this.chart) {
        this.chart.dispose()
      }
      // #endif
      // #ifdef APP-NVUE
      this.$refs.webview.evalJs(`dispose()`);
      // #endif
    },
    canvasToTempFilePath (args = {}) {
      // #ifndef APP-NVUE
      const { use2dCanvas, canvasId, canvasNode } = this;
      return new Promise((resolve, reject) => {
        const copyArgs = Object.assign({
          canvasId,
          success: resolve,
          fail: reject
        }, args);
        if (use2dCanvas) {
          delete copyArgs.canvasId;
          copyArgs.canvas = canvasNode;
        }
        uni.canvasToTempFilePath(copyArgs, this);
      });
      // #endif
      // #ifdef APP-NVUE
      this.file = ''
      this.$refs.webview.evalJs(`canvasToTempFilePath()`);
      return new Promise((resolve, reject) => {
        this.$watch('file', async (file) => {
          if (file) {
            const tempFilePath = await base64ToPath(file)
            resolve(args.success({ tempFilePath }))
          } else {
            reject(args.fail({ error: `` }))
          }
        })
      })
      // #endif
    },
    async init (callback, params) {
      // #ifndef APP-NVUE
      let config = await this.getContext();
      if (typeof callback === 'function') {
        this.chart = await callback(config);
      } else {
        const { canvas } = config
        this.chart = echarts.init(canvas, null, config);
        canvas.setChart(this.chart);
        if (this.ec && this.ec.option) {
          this.setOption(this.ec.option)
        }
      }
      // #endif
      // #ifdef APP-NVUE
      await this.getWebview()
      if (typeof callback === 'function') {
        this.$refs.webview.evalJs(`init(${JSON.stringify(callback.toString())}, ${JSON.stringify(params || this.params)})`);
      } else {
        this.$refs.webview.evalJs(`init()`);
        if (this.ec && this.ec.option) {
          this.setOption(this.ec.option)
        }
      }
      // #endif
      this.inited = true
      this.$emit('inited', true)
    },
    // #ifndef APP-NVUE
    async enabled () {
      if (this.isEnable) {
        await this.getContext();
      }
    },
    getContext () {
      const { use2dCanvas, type = '2d', config } = this;
      if (config.canvas) {
        return Promise.resolve(config);
      }
      if (use2dCanvas) {
        return new Promise(resolve => {
          uni.createSelectorQuery()
            .in(this)
            .select(`#${this.canvasId}`)
            .fields({
              node: true,
              size: true
            })
            .exec(res => {
              let { node, width, height } = res[ 0 ];
              width = width || 300;
              height = height || 300;
              const ctx = node.getContext(type);
              node.width = width * devicePixelRatio;
              node.height = height * devicePixelRatio;
              const canvas = new Canvas(ctx, this.canvasId, true, node);
              echarts.setCanvasCreator(() => canvas);
              this.canvasNode = node
              this.config = { canvas, width, height, devicePixelRatio };
              resolve(this.config);
            });
        });
      }
      return new Promise(resolve => {
        uni.createSelectorQuery()
          .in(this)
          .select(`#${this.canvasId}`)
          .boundingClientRect()
          .exec(res => {
            if (res) {
              let { width, height } = res[ 0 ];
              width = width || 300;
              height = height || 300;
              let dpr = devicePixelRatio
              // #ifdef MP-TOUTIAO
              dpr = 1.25
              // #endif
              // #ifdef MP-ALIPAY
              dpr = devicePixelRatio
              // #endif
              // #ifndef MP-ALIPAY || MP-TOUTIAO
              dpr = this.platform ? devicePixelRatio : 1
              // #endif
              this.nodeWidth = width * dpr;
              this.nodeHeight = height * dpr;
              const ctx = uni.createCanvasContext(this.canvasId, this);
              const canvas = new Canvas(ctx, this.canvasId, false);
              echarts.setCanvasCreator(() => canvas);
              this.config = { canvas, width, height, devicePixelRatio: dpr };
              resolve(this.config);
            }
          });
      });
    },
    touchStart (e) {
      if (this.chart && e.touches.length > 0) {
        var touch = e.touches[ 0 ];
        var handler = this.chart.getZr().handler;
        handler.dispatch('mousedown', {
          zrX: touch.x,
          zrY: touch.y
        });
        handler.dispatch('mousemove', {
          zrX: touch.x,
          zrY: touch.y
        });
        handler.processGesture(wrapTouch(e), 'start');
      }
    },
    touchMove (e) {
      if (this.chart && e.touches.length > 0) {
        var touch = e.touches[ 0 ];
        var handler = this.chart.getZr().handler;
        handler.dispatch('mousemove', {
          zrX: touch.x,
          zrY: touch.y
        });
        handler.processGesture(wrapTouch(e), 'change');
      }
    },
    touchEnd (e) {
      if (this.chart) {
        const touch = e.changedTouches ? e.changedTouches[ 0 ] : {};
        var handler = this.chart.getZr().handler;
        handler.dispatch('mouseup', {
          zrX: touch.x,
          zrY: touch.y
        });
        if (this.isClickable) {
          handler.dispatch('click', {
            zrX: touch.x,
            zrY: touch.y
          });
        }
        handler.processGesture(wrapTouch(e), 'end');
      }
    }
    // #endif
  }
};
</script>
<style scoped lang="scss">
.l-echart {
  position: relative;
  // #ifndef APP-NVUE
  width: 100%;
  height: 100%;
  // #endif
  // #ifdef APP-NVUE
  flex: 1;
  // #endif
  &__canvas {
    // #ifndef APP-NVUE
    width: 100%;
    height: 100%;
    // #endif
    // #ifdef APP-NVUE
    flex: 1;
    // #endif
  }
}
</style>
